import asyncio
import json
import logging as log
from typing import Any, Callable, Coroutine, List
import aiohttp
import urllib.parse
from bilibili_utils import (encWbi,getWbiKeys)


log.basicConfig(level=log.INFO)

class SpiderUtil:
    @staticmethod
    async def run_task_async(callables:List[Callable[[str,aiohttp.ClientSession],Any]],
                             paths:List[str],
                             base_url:str):
        conn = aiohttp.TCPConnector(limit=300)
        timeout=aiohttp.ClientTimeout(total=3)
        headers={
            'Referer': 'https://www.bilibili.com/',
            'Connection': 'close', 
            "User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile Safari/537.36"
        }
        cookies={
            "SESSDATA":"",
            "bili_ticket":"",
            "data_ticket":"",
            "bili_ticket_expires":""
        }
        async with aiohttp.ClientSession(base_url=base_url,
                                    timeout=timeout,
                                    headers=headers,
                                    cookies=cookies,
                                     connector=conn) as session:
            tasks = [callable(path,session) \
                     for path,callable in zip(paths,callables)]
            results = await asyncio.gather(*tasks)
            log.debug(f"执行结果：{results}")
            return results


class BilibiliSpider:

    def __init__(self,
                 user_range:range) -> None:
        self._user_range=user_range
        self.base_url="https://api.bilibili.com"

    def get_user_data_path(self):
        img_key, sub_key = getWbiKeys()
        paths = []
        for uid in self._user_range:
            signed_params = encWbi(
            params={
                'mid': uid
            },
                img_key=img_key,
                sub_key=sub_key
            )
            uri_params = urllib.parse.urlencode(signed_params)
            paths.append("/x/space/wbi/acc/info?{}".format(uri_params))
        log.info("访问路径：{}".format(paths))
        return paths
    
    async def save_user_data(self,
                             data,
                             filename:str="user_info.txt"):
        with open(filename, 'w+',encoding="utf-8") as fp:
            json.dump(data,fp,indent=2,ensure_ascii=False)
        log.info("数据保存成功！")

    async def fetch_user_data(self,
                        path:str,
                        session:aiohttp.ClientSession):
        """
        拉取用户数据
        """
        await asyncio.sleep(1)
        async with session.get(path) as response:
            json = await response.json()
            log.info(f"请求：{response.url}，\n获取用户数据：{json}")
        return json["data"]
        
            
async def main():
    bs = BilibiliSpider(range(100,110))
    paths = bs.get_user_data_path()
    callables=[]
    for _ in paths:
        callables.append(bs.fetch_user_data)
    results = await SpiderUtil.run_task_async(callables,
                                paths,
                                bs.base_url)
    await bs.save_user_data(results)

if __name__ == '__main__':
    asyncio.run(main())



